import {_decorator, AudioClip, AudioSource, director, Node} from "cc";
import {ResourceUtil} from "../utils/ResourceUtil";
import {GameContext} from "../GameContext";

const {ccclass, property} = _decorator;

@ccclass("AudioManager")
export class AudioManager {
    /**
     * 持久化音频播放节点对象
     */
    private readonly _persistRootNode: Node = null!;
    /**
     * 音频组件对象池，由于存在同时播放多个音频的场景，同时避免创建过多的音频组件
     * @private
     */
    private audioSourcePools: AudioSource[] = [];
    /**
     * 音乐音量
     */
    private musicVolume: number = 0.8;
    /**
     * 声音音量
     */
    private soundVolume: number = 1;

    /**
     * 正在加载的音频或不存在的音频
     */
    private _dictLoadingAudio: any = {}

    /**
     * 音频文件对象池，避免重复加载音频文件
     */
    private audioClipPools = {};

    private MUSIC_KEY = 'music';

    private SOUND_KEY = 'sound';

    private AUDIO_TYPE_KEY = 'type';

    private AUDIO_PATH_KEY = 'audioPath';

    constructor() {
        if (this._persistRootNode) return; //避免切换场景初始化报错
        let gameAudioNode = director.getScene()?.getChildByName("GameAudioNode");
        if (!gameAudioNode) {
            this._persistRootNode = new Node('GameAudioNode');
            // 向场景中添加背景持久化音频节点
            director.getScene()?.addChild(this._persistRootNode);
            director.addPersistRootNode(this._persistRootNode)
        }
        // 打开音频设置
        this.openAudio();
    }

    /**
     * 获取空闲音频组件
     * @param clip 音频文件
     * @private
     */
    private getIdleAudioSource(clip: AudioClip) {
        // 获取空闲的音频组件
        let idleAudioSource = this.audioSourcePools.find(audioSourcePool => !audioSourcePool.playing);
        // 若无空闲音频组件，则新建音频组件，加载至音频节点中
        if (!idleAudioSource) {
            idleAudioSource = this._persistRootNode.addComponent(AudioSource);
            // 关闭自动播放
            idleAudioSource.playOnAwake = false;
            this.audioSourcePools.push(idleAudioSource);
        }

        idleAudioSource.node.off(AudioSource.EventType.ENDED);
        idleAudioSource.clip = clip;
        idleAudioSource.currentTime = 0;
        return idleAudioSource;
    }

    /**
     * 获取音量
     * @param type
     * @private
     */
    private getVolume(type: string) {
        // 获取本地音量
        let volume = this.MUSIC_KEY === type ? this.musicVolume : this.soundVolume;
        return GameContext.STORAGE.getGlobalData(type) ? volume : 0;
    }

    /**
     * 根据类型播放音频文件
     * @param type 音乐还是音频
     * @param audioPath 音频文件
     * @param loop 是否循环
     * @private
     */
    private play(type: string, audioPath: string, loop: boolean) {
        // 获取音量
        let volume = this.getVolume(type);
        if (!volume) {
            return;
        }

        // 若正在加载，则返回，避免重复加载
        if (this._dictLoadingAudio[audioPath]) {
            return;
        }

        // 之前已加载过该文件
        if (this.audioClipPools.hasOwnProperty(audioPath)) {
            // 获取音频文件
            let audioClip = this.audioClipPools[audioPath];
            // 获取空闲的音频组件
            let idleAudioSource = this.getIdleAudioSource(audioClip);
            idleAudioSource[this.AUDIO_TYPE_KEY] = type;
            idleAudioSource[this.AUDIO_PATH_KEY] = audioPath;
            idleAudioSource.volume = volume;
            idleAudioSource.loop = loop;
            idleAudioSource.play();
            return;
        }

        // 标记此时音频文件正在加载中
        this._dictLoadingAudio[audioPath] = true;
        // 未加载过的音频文件，则准备加载
        ResourceUtil.loadRes(audioPath, AudioClip, (err: any, clip: any) => {
            // 缓存当前音频文件
            this.audioClipPools[audioPath] = clip;
            // 获取空闲音频组件
            let idleAudioSource = this.getIdleAudioSource(clip);
            idleAudioSource[this.AUDIO_TYPE_KEY] = type;
            idleAudioSource[this.AUDIO_PATH_KEY] = audioPath;
            idleAudioSource.volume = volume;
            idleAudioSource.loop = loop;
            idleAudioSource.play();
            // 移除正在加载的标记
            this._dictLoadingAudio[audioPath] = false;
        });
    }

    /**
     * 播放音乐
     * @param {String} musicPath 音乐文件路径
     * @param {Boolean} loop 是否循环播放
     */
    playMusic(musicPath: string, loop: boolean) {
        // 开始播放
        this.play(this.MUSIC_KEY, musicPath, loop);
    }

    /**
     * 播放音效
     * @param {String} soundPath 音频路径
     * @param {Boolean} loop 是否循环播放
     */
    playSound(soundPath: string, loop: boolean = false) {
        // 开始播放
        this.play(this.SOUND_KEY, soundPath, loop);
    }

    /**
     * 停止播放
     * @param audioPath
     */
    stop(audioPath: string) {
        for (let audioSourcePool of this.audioSourcePools) {
            if (audioSourcePool[this.AUDIO_PATH_KEY] === audioPath) {
                this.audioSourcePools[audioPath].stop();
            }
        }
    }

    /**
     * 停止所有的音频播放
     */
    stopAll() {
        for (let audioSourcePool of this.audioSourcePools) {
            audioSourcePool.stop();
        }
    }

    /**
     * 看广告时先将音乐暂停
     */
    pauseAll() {
        for (let audioSourcePool of this.audioSourcePools) {
            audioSourcePool.pause();
        }
    }

    /**
     * 恢复所有
     */
    resumeAll() {
        this.audioSourcePools
            // 保留循环播放的音频
            .filter(audioSource => audioSource.loop)
            .forEach(audioSourcePool => {
                // 重新播放
                audioSourcePool.play();
            })
    }

    /**
     * 设置音乐音量
     * @param volume
     */
    setMusicVolume(volume: number) {
        // 调整音乐音量
        this.musicVolume = volume;
        // 过滤所有音乐组件
        this.audioSourcePools
            .filter(audioSource => this.MUSIC_KEY === audioSource[this.AUDIO_TYPE_KEY])
            .forEach(audioSourcePool => {
                audioSourcePool.volume = this.musicVolume;
            })
    }

    /**
     * 设置音频音量
     * @param volume
     */
    setSoundVolume(volume: number) {
        this.soundVolume = volume;
        // 过滤所有音乐组件
        this.audioSourcePools
            .filter(audioSource => this.SOUND_KEY === audioSource[this.AUDIO_TYPE_KEY])
            .forEach(audioSourcePool => {
                audioSourcePool.volume = this.soundVolume;
            })
    }

    /**
     * 打开音乐
     */
    openMusic() {
        this.setMusicVolume(0.8);
        GameContext.STORAGE.setGlobalData(this.MUSIC_KEY, 'true');
    }

    /**
     * 关闭音乐
     */
    closeMusic() {
        this.setMusicVolume(0);
        GameContext.STORAGE.setGlobalData(this.MUSIC_KEY, 'false');
    }

    /**
     * 打开音频
     */
    openSound() {
        this.setSoundVolume(1);
        GameContext.STORAGE.setGlobalData(this.SOUND_KEY, 'true');
    }

    /**
     * 关闭音频
     */
    closeSound() {
        this.setSoundVolume(0);
        GameContext.STORAGE.setGlobalData(this.SOUND_KEY, 'false');
    }

    /**
     * 打开音频
     */
    openAudio() {
        this.openMusic();
        this.openSound();
    }

    /**
     * 静音
     */
    closeAudio() {
        this.closeMusic();
        this.closeSound();
    }
}